package com.flesler.stickjoy.core;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

import net.java.games.input.Controller;
import net.java.games.input.ControllerEnvironment;
import net.java.games.input.ControllerEvent;
import net.java.games.input.ControllerListener;

import com.flesler.stickjoy.core.controller.ComponentWrapper;
import com.flesler.stickjoy.core.controller.ControllerWrapper;

public final class StickJoy implements ControllerListener {
	// How often to poll
	private static final long INTERVAL = 20L;
	// Sent when a controller is removed
	private static byte REMOVED = '-';
	
	private HashMap<Byte, ControllerWrapper> controllers;
	private byte lastController;
	private StickJoyWrapper wrapper;
	
	public StickJoy(StickJoyWrapper wrapper)
	{
		this.wrapper = wrapper;
		init();
	}
	
	private void init()
	{
		controllers = new HashMap<Byte, ControllerWrapper>();
		ControllerEnvironment env = ControllerEnvironment.getDefaultEnvironment();
		env.addControllerListener(this);

		for (Controller controller : env.getControllers()) 
			add(controller);
	}
	
	public void sendAll()
	{
		for (ControllerWrapper controller : controllers.values())
			send(controller, controller.getComponents());
		wrapper.flush();
	}
	
	public void startPolling()
	{
		while (true) {
			poll();
			try {
				Thread.sleep(INTERVAL);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void poll()
	{
		boolean hasChanges = false;
		for (ControllerWrapper controller : controllers.values())
		{
			if (!controller.poll()) 
			{
				remove(controller);
				continue;
			}
			
			Collection<ComponentWrapper> components = controller.getChangedComponents();
			if (components.isEmpty())
				continue;
			
			hasChanges = true;
			send(controller, components);
		}
		
		if (hasChanges)
			wrapper.flush();
	}
	
	private void send(ControllerWrapper controller, Collection<ComponentWrapper> components)
	{
		writeController(controller);
		for (ComponentWrapper component : components)
		{
			writeByte(component.getState());
			writeByte(component.getKey());
		}
	}
	
	private void writeController(ControllerWrapper controller)
	{
		// Only send controller key if changed from last time
		byte id = controller.getId();
		if (id != lastController)
		{
			lastController = id;
			writeByte(id);
		}
	}
	
	private void writeByte(byte b)
	{
		try {
			wrapper.getStream().write(b);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void add(Controller controller)
	{
		if (ControllerWrapper.isValid(controller))
		{
			ControllerWrapper wrapper = new ControllerWrapper(controller);
			controllers.put(wrapper.getId(), wrapper);
		}
	}
	
	private void remove(ControllerWrapper controller)
	{
		writeController(controller);
		writeByte(REMOVED);
		
		controllers.remove(controller.getId());
		controller.destroy();
	}

	// ControllerListener implementation, it's not really implemented yet on JInput!
	
	public void controllerAdded(ControllerEvent event)
	{
		add(event.getController());
	}

	public void controllerRemoved(ControllerEvent event)
	{
		// Handled by poll() returning false
	}
	
}
